﻿/**
 * @file	Key.as
 * @author	Diego Maximiliano Furque, Jorge Miranda
 * @version 1.0.0
*/

/*
 * History:
 * 0.1.0 - First Release - Diego Maximiliano Furque
 * 0.1.1 - Added key codes - Jorge Miranda
 * 0.1.3 - Unstable version - Jorge Miranda
 * 1.0.0 - AS3 version - Jorge Miranda
*/
package racingGame.core { 
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.Event;
    import flash.events.TimerEvent;
	import flash.display.Stage;
	import flash.utils.Timer;
	
	import flash.utils.*;
	
	public class Key extends EventDispatcher
	{
		/**
		* Constants
		*/
		
		public static const	DEF_KEY_LIFE		:Number = 1000;
		
		public static const	MAX_KEYS_CODE		:Number	= 90;	
		
		public static const	dispatcher  		:EventDispatcher = new EventDispatcher();		
		
		public static const	A	: Number = 	65	;
		public static const	B	: Number = 	66	;
		public static const	C	: Number = 	67	;
		public static const	D	: Number = 	68	;
		public static const	E	: Number = 	69	;
		public static const	F	: Number = 	70	;
		public static const	G	: Number = 	71	;
		public static const	H	: Number = 	72	;
		public static const	I	: Number = 	73	;
		public static const	J	: Number = 	74	;
		public static const	K	: Number = 	75	;
		public static const	L	: Number = 	76	;
		public static const	M	: Number = 	77	;
		public static const	N	: Number = 	78	;
		public static const	O	: Number = 	79	;
		public static const	P	: Number = 	80	;
		public static const	Q	: Number = 	81	;
		public static const	R	: Number = 	82	;
		public static const	S	: Number = 	83	;
		public static const	T	: Number = 	84	;
		public static const	U	: Number = 	85	;
		public static const	V	: Number = 	86	;
		public static const W	: Number = 	87	;
		public static const	X	: Number = 	88	;
		public static const	Y	: Number = 	89	;
		public static const	Z	: Number = 	90	;
		public static const	n0	: Number = 	48	;
		public static const	n1	: Number = 	49	;
		public static const	n2	: Number = 	50	;
		public static const	n3	: Number = 	51	;
		public static const	n4	: Number = 	52	;
		public static const	n5	: Number = 	53	;
		public static const	n6	: Number = 	54	;
		public static const	n7	: Number = 	55	;
		public static const	n8	: Number = 	56	;
		public static const	n9	: Number = 	57	;
		public static const	a	: Number = 	65	;
		public static const	b	: Number = 	66	;
		public static const	c	: Number = 	67	;
		public static const	d	: Number = 	68	;
		public static const	e	: Number = 	69	;
		public static const	f	: Number = 	70	;
		public static const	g	: Number = 	71	;
		public static const	h	: Number = 	72	;
		public static const	i	: Number = 	73	;
		public static const	j	: Number = 	74	;
		public static const	k	: Number = 	75	;
		public static const	l	: Number = 	76	;
		public static const	m	: Number = 	77	;
		public static const	n	: Number = 	78	;
		public static const	o	: Number = 	79	;
		public static const	p	: Number = 	80	;
		public static const	q	: Number = 	81	;
		public static const	r	: Number = 	82	;
		public static const	s	: Number = 	83	;
		public static const	t	: Number = 	84	;
		public static const	u	: Number = 	85	;
		public static const	v	: Number = 	86	;
		public static const	w	: Number = 	87	;
		public static const	x	: Number = 	88	;
		public static const	y	: Number = 	89	;
		public static const	z	: Number = 	90	;
		public static const	np0	: Number = 	96	;
		public static const	np1	: Number = 	97	;
		public static const	np2	: Number = 	98	;
		public static const	np3	: Number = 	99	;
		public static const	np4	: Number = 	100	;
		public static const	np5	: Number = 	101	;
		public static const	np6	: Number = 	102	;
		public static const	np7	: Number = 	103	;
		public static const	np8	: Number = 	104	;
		public static const	np9	: Number = 	105	;
		public static const	MULTIPLY	: Number = 	106	;
		public static const	ADD			: Number = 	107	;
		public static const	ENTER		: Number = 	13	;
		public static const	SUBTRACT	: Number = 	109	;
		public static const	DECIMAL		: Number = 	110	;
		public static const	DIVIDE		: Number = 	111	;
		public static const	F1	: Number = 	112	;
		public static const	F2	: Number = 	113	;
		public static const	F3	: Number = 	114	;
		public static const	F4	: Number = 	115	;
		public static const	F5	: Number = 	116	;
		public static const	F6	: Number = 	117	;
		public static const	F7	: Number = 	118	;
		public static const	F8	: Number = 	119	;
		public static const	F9	: Number = 	120	;
		public static const	F11	: Number = 	122	;
		public static const	F12	: Number = 	123	;
		public static const	F13	: Number = 	124	;
		public static const	F14	: Number = 	125	;
		public static const	F15	: Number = 	126	;
		public static const	BACKSPACE	: Number = 	8	;
		public static const	TAB			: Number = 	9	;
		public static const	SHIFT		: Number = 	16	;
		public static const	CONTROL		: Number = 	17	;
		public static const	CAPSLOCK	: Number = 	20	;
		public static const	ESC			: Number = 	27	;
		public static const	SPACEBAR	: Number = 	32	;
		public static const	PAGEUP		: Number = 	33	;
		public static const	PAGEDOWN	: Number = 	34	;
		public static const	END			: Number = 	35	;
		public static const	HOME		: Number = 	36	;
		public static const	LEFT		: Number = 	37	;
		public static const	UP			: Number = 	38	;
		public static const	RIGHT		: Number = 	39	;
		public static const	DOWN		: Number = 	40	;
		public static const	INSERT		: Number = 	45	;
		public static const	DELETE		: Number = 	46	;
		public static const	NUMLOCK		: Number = 	144	;
		public static const	SCRLK		: Number = 	145	;
	
	

		private static 		var _keysDown	:Object = new Object();  // stores key codes of all keys pressed
		private static 		var _initialized:Boolean = false;  // marks whether or not the class has been initialized
		
		// Properties
	
		// Cheat functionality stuff 		
		protected static	var _timer		:Timer;
		protected static	var _cheatcode	:String;
		protected static	var _cheats		:Array;
	
		protected static 	var _nKeyLife	:Number;
		
		static public function get initialized():Boolean { return _initialized; }
		
		
       
	   
		
		
			
		public static function initialize(stage:Stage) :void {
			
			 if (!_initialized) {
                // assign listeners for key presses and deactivation of the player
                stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
                stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
                stage.addEventListener(Event.DEACTIVATE, clearKeys);             
                
				
				// Initialize the cheatcode
				_cheatcode = "   ";
				
				// Cheats's array
				_cheats = new Array ();

				
				// Adds a listener for the keyboard combos.			
				stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);

			
				_nKeyLife = DEF_KEY_LIFE;
			
				// Function that automatically updates the cheatcode.		
				_timer = new Timer(_nKeyLife, 0);
				_timer.addEventListener("timer", updateCheatCode);
				_timer.start();
				
				// mark initialization as true so redundant
                // calls do not reassign the event handlers
                _initialized = true;
            }			
		}
		
		
		
		
		/**
		 * Adds a new cheat to the Console.
		 * 
		 * @param	txt		Text that executes the cheat
		 * @param	pointer	Pointer to the function.
		 * @return 	void
		*/
		
		public static function addCheat(txt:String, pointer:Function) :void
		{
			for (var i :* in _cheats)
			{
				if (_cheats[i]["name"] == txt)
				{
					//TODO: Through exception.
					return;
				}
			}
			
			if (pointer == null)
			{
				//TODO: Through exception.
				return;
			}
			
			var p :Object = new Object ();
			txt = txt.toUpperCase();
	
			p["name"] = txt;
			p["pointer"] = pointer;
			
			_cheats.push (p);		
	
		}
		
		
		/**
		 * Searchs and executes a cheat.
		 * 
		 * @return 	void
		*/
		
		private static function searchCheat() :void {
			
			for(var i :* = 0; i<_cheats.length; i++)
			{
				if(_cheatcode.indexOf(_cheats[i]["name"]) != -1)
				{
					_cheats[i]["pointer"]();
					// Borro todo el contenido de _cheatcode
					// para que no lo realize si vuelvo a presionar 
					//una tecla cualquiera
					_cheatcode = "   ";
					return;					
				}
			}			
		}
		
		/**
		 * Deletes de first character in _cheatcode.
		 * 
		 * @return 	void
		*/
		
		private static function updateCheatCode(e:TimerEvent) :void {
			
			var n :Number = _cheatcode.length
			
			if(n > 10)
			{
				_cheatcode = _cheatcode.substring(n-10)
			}
			else
			{
				_cheatcode = _cheatcode.substring(1)
			}
		}
		
		
		private static function keyDown(e:KeyboardEvent) :void {
			
			var nKey :uint = e.keyCode;
			
			/** Buscar "Cheats" **/
			_cheatcode = _cheatcode + String.fromCharCode(nKey);	
			
			searchCheat();
		}
		
		
		/**
         * Returns true or false if the key represented by the
         * keyCode passed is being pressed
         */
        public static function isDown(keyCode:uint):Boolean {
			
            if (!_initialized) {
                // throw an error if isDown is used
                // prior to Key class initialization
                throw new Error("Key class has yet been initialized.");
            }
            return Boolean(keyCode in _keysDown);
        }
       
	   
	   
        /**
         * Event handler for capturing keys being pressed
         */
        private static function keyPressed(event:KeyboardEvent):void {			
			
            // create a property in _keysDown with the name of the keyCode
            _keysDown[event.keyCode] = true;
			
			dispatcher.dispatchEvent(event);
        }
       
	   
	   
        /**
         * Event handler for capturing keys being released
         */
		private static function keyReleased(event:KeyboardEvent):void {			
			
            if (event.keyCode in _keysDown) {
                // delete the property in _keysDown if it exists
                delete _keysDown[event.keyCode];
            }
			
			dispatcher.dispatchEvent(event);
        }
    
	
	
        /**
         * Event handler for Flash Player deactivation
         */
        private static function clearKeys(event:Event):void {
            // clear all keys in _keysDown since the player cannot
            // detect keys being pressed or released when not focused
            _keysDown = new Object();
        }
		
		
		

	}
}

//Because ActionScript 3.0 is based on ECMAScript...
internal class SingletonEnforcer {}